package com.zhz.common.feign;

import cn.hutool.core.lang.Assert;
import feign.Request;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;

import java.util.concurrent.TimeUnit;
import java.util.function.Function;


/**
 * FeignOptionsUtil
 * <p>
 * 设置 feign 接口调用的超时时间。<br>
 * 注意：仅对下一次 feign 调用有效，若在循环中调用 feign 接口，也要在循环中调用 feign 接口前设置对应的超时时间。
 *
 * @author zhouhengzhe
 */
public abstract class FeignOptionsUtil {

    public static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT / 1000;
    public static final int DEFAULT_READ_TIMEOUT_SECONDS = RibbonClientConfiguration.DEFAULT_READ_TIMEOUT / 1000;
    public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;


    /**
     * 设置本次 feign 调用连接超时时间
     *
     * @param connectTimeoutSeconds 超时时间（秒）
     */
    public static void setConnectTimeout(int connectTimeoutSeconds) {
        setOptions(connectTimeoutSeconds, DEFAULT_READ_TIMEOUT_SECONDS);
    }


    /**
     * 设置本次 feign 调用响应超时时间
     *
     * @param readTimeoutSeconds 超时时间（秒）
     */
    public static void setReadTimeout(int readTimeoutSeconds) {
        setOptions(DEFAULT_CONNECT_TIMEOUT_SECONDS, readTimeoutSeconds);
    }


    /**
     * 设置超时时间
     *
     * @param connectTimeoutSeconds 连接超时（秒）
     * @param readTimeoutSeconds    响应超时（秒）
     */
    public static void setOptions(int connectTimeoutSeconds, int readTimeoutSeconds) {
        Assert.isTrue(connectTimeoutSeconds > 0, "feign connect timeout must be positive");
        Assert.isTrue(readTimeoutSeconds > 0, "feign read timeout must be positive");
        Request.Options options = new Request.Options(connectTimeoutSeconds, TimeUnit.SECONDS, readTimeoutSeconds, TimeUnit.SECONDS, DEFAULT_FOLLOW_REDIRECTS);
        setOptions(options);
    }


    public static void setOptions(Request.Options options) {
        FeignOptionsInterceptor.OPTIONS_HOLDER.set(options);
    }


    public static void clear() {
        FeignOptionsInterceptor.OPTIONS_HOLDER.remove();
    }


    /**
     * 为 feign api 接口设置超时时间
     *
     * @param feignApi           feign 接口
     * @param readTimeoutSeconds 超时时间
     * @return
     */
    public static <T, R> Function<T, R> wrap(Function<T, R> feignApi, int readTimeoutSeconds) {
        return t -> {
            try {
                setReadTimeout(readTimeoutSeconds);
                return feignApi.apply(t);
            } finally {
                clear();
            }
        };
    }

}
